/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2019-2020. All rights reserved.
 */

import lombok.extern.slf4j.Slf4j;
import org.pf4j.ClassLoadingStrategy;
import org.pf4j.DefaultPluginManager;
import org.pf4j.JarPluginLoader;
import org.pf4j.ManifestPluginDescriptorFinder;
import org.pf4j.PluginClassLoader;
import org.pf4j.PluginDescriptor;
import org.pf4j.PluginDescriptorFinder;
import org.pf4j.PluginManager;

import java.nio.file.Path;
import java.security.AccessController;
import java.security.PrivilegedAction;

/**
 * jar插件管理工具类
 *
 * @author sunzequan
 * @since 2019-09-23
 */
@Slf4j
public class JarPluginManager extends DefaultPluginManager {
    /**
     * construct me with the given classloader
     *
     * @param classLoader 类加载器
     */
    public JarPluginManager(ClassLoader classLoader) {
        ParentClassLoaderJarPluginLoader.parentClassLoader = classLoader;
    }

    @Override
    protected JarPluginLoader createPluginLoader() {
        // load only jar plugins
        return new ParentClassLoaderJarPluginLoader(this);
    }

    @Override
    protected PluginDescriptorFinder createPluginDescriptorFinder() {
        // read plugin descriptor from jar's manifest
        return new ManifestPluginDescriptorFinder();
    }

    /**
     * jar包加载 ParentClassLoaderJarPluginLoader
     */
    public static class ParentClassLoaderJarPluginLoader extends JarPluginLoader {
        static ClassLoader parentClassLoader;

        /**
         * @param pluginManager 插件manager
         */
        public ParentClassLoaderJarPluginLoader(PluginManager pluginManager) {
            super(pluginManager);
        }

        @Override
        public ClassLoader loadPlugin(Path pluginPath, PluginDescriptor pluginDescriptor) {
            // 修改pf4j默认样例代码. 每个插件有自己的pluginClassLoader, 否则插件加载同一名称配置文件会加载错误
            return (PluginClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
                @Override
                public Object run() {
                    PluginClassLoader pluginClassLoader =
                            new PluginClassLoader(pluginManager, pluginDescriptor, parentClassLoader,
                                    ClassLoadingStrategy.PDA);
                    pluginClassLoader.addFile(pluginPath.toFile());
                    return pluginClassLoader;
                }
            });
        }
    }
}
